<?php
namespace cosocket\system\server\websocket;

use cosocket\system\Debug;
use Swoole\Coroutine\Http\Server as CoHttpServer;
use Swoole\Coroutine\Scheduler;
use Swoole\Runtime;
use Swoole\Process;
use cosocket\system\Logger;
use cosocket\system\server\abstracts\AbstractWebSocket;
use cosocket\system\server\interfaces\WebSocketInterface;

class CoWebSocket extends AbstractWebSocket implements WebSocketInterface
{
    public function run()
    {
        Runtime::enableCoroutine(SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL);
        $sch = new Scheduler;
        $sch->set($this->config);
        $sch->add(function () {
            //TODO: 初始化的代码可以写在这里

            //end

            // 初始化服务器
            $this->initializeServer();
            $this->start();
        });
        $sch->start();
    }

    public function initializeServer()
    {
        echo "协程模式启动,监听端口: " . $this->port . "\n";

        $this->ws = new CoHttpServer($this->host, $this->port, false);

        Process::signal(SIGTERM, function () {
            $this->ws->shutdown();
        });

        $this->ws->handle("/ws", function ($request, $ws) {
            $this->handleRequest($request, $ws);
        });

        echo "\n初始化成功\n\n";
    }

    public function start()
    {
        $r = $this->ws->start();
        Debug::info("WebSocket Server Start, Return=[". $r. "]");
    }
    public function handleRequest($request, $ws)
    {
        try {
            if (strtolower($request->header['connection']) != 'upgrade') {
                return;
            }

            $ws->upgrade();
            while (true) {
                $frame = $ws->recv();
                if (empty($frame)) {
                    break;
                }
                $this->handleOpcode($frame, $ws, $request);
            }
        } catch (\Exception $e) {
            Logger::error("Catch An Exception File=[" . $e->getFile() . "|" . $e->getLine() . "] Code=[" . $e->getCode() . "], Message=[" . $e->getMessage() . "]");
        }
    }

    public function handleOpcode($frame, $ws, $request)
    {
        switch ($frame->opcode) {
            case 1: // Text frame
                if (!empty($frame->data)) {
                    $this->processFrame($frame, $ws, $request);
                } else if ($frame->data === "") {
                    $this->handleClose($frame, $ws, "Data Is Empty Close frame");
                }
                break;
            case 8: // Close frame
                $this->handleClose($frame, $ws, "Close frame");
                break;
        }
    }
}